package common.util;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.yuntsg.ruiijn.databse_search.entity.FileDetails;
import com.yuntsg.ruiijn.databse_search.service.FeatureService;
import common.bean.RRException;
import common.vo.Scenario;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static cn.hutool.core.img.ImgUtil.*;
import static org.opencv.imgcodecs.Imgcodecs.imwrite;


@Component
@Slf4j

public class CutImageUtil {

    @Autowired
    private MinioUtil minioUtil;
    @Autowired
    private ExtractUtil extractUtil;
    @Autowired
    private FeatureService featureService;
    @Autowired
    private NacosUtil nacosUtil;


    @Value("${upload.tempPath}")
    public String tempPath;


    @Value("${central.with-opencv}")
    public boolean withOpenCV;

    static List<MatInfo> cutImageByX(List<MatInfo> matInfos, Integer X_threshold, Integer min) {
        List<MatInfo> matList = new ArrayList<>();
        for (MatInfo matInfo : matInfos) {
            int nWidth = matInfo.getDst().width();
            int nHeight = matInfo.getDst().height();
            Integer blackLine = 0;

            List<Coordinate> coordinateList = new ArrayList<>();

            Integer startX = 0;
            Boolean blackArea = false;

            for (int x = 0; x < nWidth; x++) {
                Set<Boolean> blackSet = new HashSet<>();
                for (int y = 0; y < nHeight; y++) {
                    double[] doubles = matInfo.getDst().get(y, x);
                    if (doubles[0] < 1) {
                        blackSet.add(true);
                    } else {
                        blackSet.add(false);
                    }
                }

                if (!blackSet.contains(false)) {
                    blackLine++;
                } else {
                    if (blackArea) {
                        startX = x;
                        blackArea = false;
                    }
                    blackLine = 0;
                }

                if (!blackArea && blackLine > X_threshold) {
                    blackArea = true;
                    if (x - blackLine + 1 > 0) {
                        Integer stopX = x - blackLine + 1 - startX;
                        if (stopX > min) {
                            coordinateList.add(new Coordinate(startX, 0, x - blackLine + 1, nHeight));
                        }
                    }

                }
            }
            if (!blackArea && nWidth - startX > min) {
                coordinateList.add(new Coordinate(startX, 0, nWidth, nHeight));
            }

            for (Coordinate coordinate : coordinateList) {
                MatInfo yMatInfo = new MatInfo();
                yMatInfo.setSrc(new Mat(matInfo.getSrc(), new Rect(coordinate.getStartX(), coordinate.getStartY(), coordinate.getStopX() - coordinate.getStartX(), coordinate.getStopY() - coordinate.getStartY())));
                yMatInfo.setDst(new Mat(matInfo.getDst(), new Rect(coordinate.getStartX(), coordinate.getStartY(), coordinate.getStopX() - coordinate.getStartX(), coordinate.getStopY() - coordinate.getStartY())));
                yMatInfo.setTopX(coordinate.getStartX() + matInfo.getTopX());
                yMatInfo.setTopY(coordinate.getStartY() + matInfo.getTopY());
                yMatInfo.setBottomX(coordinate.getStopX() + matInfo.getTopX());
                yMatInfo.setBottomY(coordinate.getStopY() + matInfo.getTopY());
                matList.add(yMatInfo);
            }
        }
        return matList;
    }

    static List<MatInfo> cutImageByY(List<MatInfo> matInfos, Integer Y_threshold, Integer min) {
        List<MatInfo> matList = new ArrayList<>();
        for (MatInfo matInfo : matInfos) {
            int nWidth = matInfo.getDst().width();
            int nHeight = matInfo.getDst().height();
            Integer blackLine = 0;

            List<Coordinate> coordinateList = new ArrayList<>();


            Integer startY = 0;
            Boolean blackArea = false;
            for (int y = 0; y < nHeight; y++) {
                Set<Boolean> blackSet = new HashSet<>();
                for (int x = 0; x < nWidth; x++) {
                    double[] doubles = matInfo.getDst().get(y, x);
                    if (doubles[0] < 1) {
                        blackSet.add(true);
                    } else {
                        blackSet.add(false);
                    }
                }

                if (!blackSet.contains(false)) {
                    blackLine++;
                } else {
                    if (blackArea) {
                        startY = y;
                        blackArea = false;
                    }
                    blackLine = 0;
                }

                if (!blackArea && blackLine > Y_threshold) {
                    blackArea = true;
                    if (y - blackLine + 1 > 0) {
                        Integer stopY = y - blackLine + 1 - startY;
                        if (stopY > min) {
                            coordinateList.add(new Coordinate(0, startY, nWidth, y - blackLine + 1));
                        }
                    }
                }
            }
            if (!blackArea && nHeight - startY > min) {
                coordinateList.add(new Coordinate(0, startY, nWidth, nHeight));
            }

            for (Coordinate coordinate : coordinateList) {
                MatInfo yMatInfo = new MatInfo();
                yMatInfo.setSrc(new Mat(matInfo.getSrc(), new Rect(coordinate.getStartX(), coordinate.getStartY(), coordinate.getStopX() - coordinate.getStartX(), coordinate.getStopY() - coordinate.getStartY())));
                yMatInfo.setDst(new Mat(matInfo.getDst(), new Rect(coordinate.getStartX(), coordinate.getStartY(), coordinate.getStopX() - coordinate.getStartX(), coordinate.getStopY() - coordinate.getStartY())));
                yMatInfo.setTopX(coordinate.getStartX() + matInfo.getTopX());
                yMatInfo.setTopY(coordinate.getStartY() + matInfo.getTopY());
                yMatInfo.setBottomX(coordinate.getStopX() + matInfo.getTopX());
                yMatInfo.setBottomY(coordinate.getStopY() + matInfo.getTopY());
                matList.add(yMatInfo);
            }
        }
        return matList;
    }

    public static boolean isBlack(Mat src) {
        Integer blackNum = 0;
        Integer whiteNum = 0;

        for (int i = 0; i < src.height(); i++) {
            for (int s = 0; s < src.width(); s++) {
                double[] doubles = src.get(i, s);
                if (doubles[0] < 1) {
                    blackNum++;
                } else if (doubles[0] > 254) {
                    whiteNum++;
                }
            }
        }
        if (blackNum / (whiteNum == 0 ? 1 : whiteNum) > 1000) {
            return false;
        }
        return true;
    }

    /**
     * 手动上传UID
     *
     * @param path
     * @param fid
     * @param rid
     * @param source
     * @return
     */
    public List<FileDetails> workNoUid(String path, Integer fid, Integer rid, Integer source, Integer uid) {

        if (withOpenCV) {
            try {
                return nacosUtil.workNoUid(path, fid, rid, source, uid);
            } catch (Exception e) {
                log.info("open提取失败,走本地流程");
            }
        }

        List<FileDetails> fileDetailsList = new ArrayList<>();
        String ext = path.substring(path.lastIndexOf("."));
        Mat src = Imgcodecs.imdecode(new MatOfByte(minioUtil.downloadByte(path)), Imgcodecs.IMREAD_COLOR);
        if (!src.empty() && src.width() > 100 && src.height() > 100) {
            Size ksize = new Size(3, 3);
            Mat gas = new Mat();
            Mat dst = new Mat();
            Mat gry = new Mat();
            Imgproc.GaussianBlur(src, gas, new Size(3, 3), 0, 0);
            Imgproc.cvtColor(gas, gry, Imgproc.COLOR_BGR2GRAY);
            Imgproc.adaptiveThreshold(gry, dst, 255, 0, 1, 15, 10);

            List<MatInfo> matInfos = new ArrayList<>();
            matInfos.add(new MatInfo(src, dst, 0, 0, 0, 0));
            for (int i = 0; i < 2; i++) {
                matInfos = cutImageByY(matInfos, 5, 10);
                if (i == 0 && matInfos.size() > 30) {
                    matInfos = new ArrayList<>();
                    break;
                }
                matInfos = cutImageByX(matInfos, 5, 10);
                if (i == 0 && matInfos.size() > 100) {
                    matInfos = new ArrayList<>();
                    break;
                }
            }

            for (int i = 0; i < matInfos.size(); i++) {
                if (matInfos.get(i).getSrc().height() > 120 || matInfos.get(i).getSrc().width() > 120) {
                    String tempPath2 = tempPath + fid.toString() + "_" + i + ext;
                    imwrite(tempPath2, matInfos.get(i).getSrc());
                    String temp = null;
                    if (source == 3) {
                        temp = minioUtil.upload(FileUtil.getInputStream(new File(tempPath2)), 99999999, DateUtil.today(), fid + "_" + i + ext, Scenario.temp);
                    } else {
                        temp = minioUtil.upload(FileUtil.getInputStream(new File(tempPath2)), uid, DateUtil.today(), fid + "_" + i + ext, Scenario.temp);
                    }
                    FileDetails fileDetails = new FileDetails();
                    fileDetails.setFId(fid);
                    fileDetails.setPath(temp);
                    fileDetails.setRid(rid);
//                    fileDetails.setCollectionStatus(source);
                    Integer type = featureService.imageClassify(temp);
                    fileDetails.setTpye(type);
                    if (type == 4) {
                        fileDetails.setDeleteFlag(featureService.imageClassifyStep(temp));
                    } else {
                        fileDetails.setDeleteFlag(-1);
                    }
                    fileDetails.setTopX(matInfos.get(i).getTopX());
                    fileDetails.setTopY(matInfos.get(i).getTopY());
                    fileDetails.setBottomX(matInfos.get(i).getBottomX());
                    fileDetails.setBottomY(matInfos.get(i).getBottomY());
                    fileDetailsList.add(fileDetails);
                }
            }
        }

        return fileDetailsList;

    }

    public List<FileDetails> workfiligreeNoId(String path, Integer fid, Integer rid, Integer source, Integer uid) {

        if (withOpenCV) {
            try {
                return nacosUtil.workfiligreeNoId(path, fid, rid, source, uid);
            } catch (Exception e) {
                log.info("opencv提取失败,走本地流程");
            }
        }

        List<FileDetails> fileDetailsList = new ArrayList<>();

        String ext = path.substring(path.lastIndexOf("."));
        Mat src = Imgcodecs.imdecode(new MatOfByte(minioUtil.downloadByte(path)), Imgcodecs.IMREAD_COLOR);

        Integer num = 4;
        Integer x_threshold = 1;
        Integer y_threshold = 1;


        if (!src.empty() && src.width() > 100 && src.height() > 100) {
            Size ksize = new Size(3, 3);
            Mat gas = new Mat();
            Mat dst = new Mat();
            Mat gry = new Mat();
            Imgproc.GaussianBlur(src, gas, ksize, 0, 0);
            Imgproc.cvtColor(gas, gry, Imgproc.COLOR_BGR2GRAY);
            Imgproc.adaptiveThreshold(gry, dst, 255, 0, 1, 15, 10);

            List<MatInfo> matInfos = new ArrayList<>();
            matInfos.add(new MatInfo(src, dst, 0, 0, 0, 0));
            for (int i = 0; i < 4; i++) {
                matInfos = cutImageByY(matInfos, y_threshold, 0);
//                if (i == 0 && matInfos.size() > 30) {
//                    matInfos = new ArrayList<>();
//                    break;
//                }
                matInfos = cutImageByX(matInfos, x_threshold, 0);
//                if (i == 0 && matInfos.size() > 100) {
//                    matInfos = new ArrayList<>();
//                    break;
//                }
            }

            for (int i = 0; i < matInfos.size(); i++) {

                String coordinate = matInfos.get(i).getTopX() + "," + matInfos.get(i).getTopY() + "," + matInfos.get(i).getBottomX() + "," + matInfos.get(i).getBottomY();
                int h = matInfos.get(i).getSrc().height();
                int w = matInfos.get(i).getSrc().width();
                if (w > 44 && h > 7) {
                    if (w / h > 34 || h / w > 34) {
                    } else {

                        String tempPath2 = tempPath + fid.toString() + "_" + i + ext;
                        imwrite(tempPath2, matInfos.get(i).getSrc());

                        String temp = minioUtil.upload(FileUtil.getInputStream(new File(tempPath2)), uid, DateUtil.today(), fid + "_" + i + ext, Scenario.temp);
                        FileDetails fileDetails = new FileDetails();
                        fileDetails.setFId(fid);
                        fileDetails.setPath(temp);
                        fileDetails.setRid(rid);
//                        fileDetails.setCollectionStatus(source);
                        Integer type = featureService.imageClassify(temp);
                        if (type == 4) {
                            fileDetails.setDeleteFlag(featureService.imageClassifyStep(temp));
                        } else {
                            fileDetails.setDeleteFlag(-1);
                        }
                        fileDetails.setTpye(7);
                        fileDetails.setTopX(matInfos.get(i).getTopX());
                        fileDetails.setTopY(matInfos.get(i).getTopY());
                        fileDetails.setBottomX(matInfos.get(i).getBottomX());
                        fileDetails.setBottomY(matInfos.get(i).getBottomY());
                        fileDetailsList.add(fileDetails);
                    }
                }

            }
        }

        return fileDetailsList;
    }

    /**
     * 等分切割图片
     *
     * @param parts     0 - 4
     * @param direction 0 横切 1 纵切
     * @return
     */
    public List<FileDetails> imgClice(Integer parts, Integer direction, FileDetails fileDetails, String userId) {
        List<FileDetails> result = new ArrayList<>();

        String path = fileDetails.getPath();
        File destDir = FileUtil.file(StrUtil.subBefore(tempPath + path, ".", true) + "/" + direction + "/" + parts);
//        File srcImage = FileUtil.file(LOCAL_FILE_PATH + path);

        if (false == destDir.exists()) {
            FileUtil.mkdir(destDir);
        } else if (false == destDir.isDirectory()) {
            throw new IllegalArgumentException("Destination Dir must be a Directory !");
        }

        Integer topX = fileDetails.getTopX();
        Integer topY = fileDetails.getTopY();
        Integer bottomX = fileDetails.getBottomX();
        Integer bottomY = fileDetails.getBottomY();
        try {
            // 读取源图像
            Mat src = Imgcodecs.imdecode(new MatOfByte(minioUtil.downloadByte(path)), Imgcodecs.IMREAD_COLOR);
            final BufferedImage bi = extractUtil.Mat2BufferedImage(src);
            int srcWidth = bi.getWidth(); // 源图宽度
            int srcHeight = bi.getHeight(); // 源图高度


            int dest = direction == 1 ? NumberUtil.partValue(srcWidth, parts) : NumberUtil.partValue(srcHeight, parts);
            // 循环建立切片
            Image tag;
            if (direction == 0) {
                for (int i = 0; i < parts; i++) {
                    tag = cut(bi, new Rectangle(0, i * dest, srcWidth, dest));
                    // 输出为文件
                    ImageIO.write(toRenderedImage(tag), IMAGE_TYPE_JPEG, new File(destDir, i + ".jpg"));
                    String docx = minioUtil.upload(FileUtil.getInputStream(destDir.getPath() + "/" + i + ".jpg"), Integer.parseInt(userId), DateUtil.today(), StrUtil.subBefore(StrUtil.subAfter(path, "/", true), ".", true) + "/" + direction + "/" + parts + "/" + i + ".jpg", Scenario.temp);
                    FileDetails fileDetailsin = new FileDetails();
                    fileDetailsin.setFId(fileDetails.getId());
                    fileDetailsin.setRid(fileDetails.getRid());
                    fileDetailsin.setTpye(8);
                    fileDetailsin.setPmid(fileDetails.getPmid());
                    fileDetailsin.setStatus(fileDetails.getStatus());

                    fileDetailsin.setPath(docx);
                    fileDetailsin.setTopX(topX);
                    fileDetailsin.setTopY(topY + (i * dest));
                    fileDetailsin.setBottomX(bottomX);
                    fileDetailsin.setBottomY(topY + ((i + 1) * dest));
                    fileDetailsin.setPartDirct(parts.toString() + direction.toString());
                    result.add(fileDetailsin);
                    FileUtil.del(destDir.getPath() + "/" + i + ".jpg");
                }
            } else {
                for (int i = 0; i < parts; i++) {
                    tag = cut(bi, new Rectangle(i * dest, 0, dest, srcHeight));
                    // 输出为文件
                    ImageIO.write(toRenderedImage(tag), IMAGE_TYPE_JPEG, new File(destDir, i + ".jpg"));
                    String docx = minioUtil.upload(FileUtil.getInputStream(destDir.getPath() + "/" + i + ".jpg"), Integer.parseInt(userId), DateUtil.today(), i + ".jpg", Scenario.view);
                    FileDetails fileDetailsin = new FileDetails();
                    fileDetailsin.setFId(fileDetails.getFId());
                    fileDetailsin.setRid(fileDetails.getRid());
                    fileDetailsin.setTpye(8);
                    fileDetailsin.setPmid(fileDetails.getPmid());
                    fileDetailsin.setStatus(fileDetails.getStatus());

                    fileDetailsin.setPath(docx);
                    fileDetailsin.setTopX(topX + (i * dest));
                    fileDetailsin.setTopY(topY);
                    fileDetailsin.setBottomX(topX + ((i + 1) * dest));
                    fileDetailsin.setBottomY(bottomY);
                    fileDetailsin.setPartDirct(parts.toString() + direction.toString());

                    result.add(fileDetailsin);
                    FileUtil.del(destDir.getPath() + "/" + i + ".jpg");
                }
            }

        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
        return result;
    }

    public void colorInversion(String path) {
        Mat src = Imgcodecs.imdecode(new MatOfByte(minioUtil.downloadByte(path)), Imgcodecs.IMREAD_COLOR);
        if (src.empty()) {
            throw new RRException("no file");
        }
        Mat dst = new Mat();
        src.copyTo(dst);
        FileUtil.del(path);
        Core.bitwise_not(src, dst);
        imwrite(path, dst);
    }

}
